
/***************************************************************************
 *   jell - library                                                        *
 *                                                                         *
 *   Copyright (C) 2008 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/

#ifndef JEFI_JELLY_HPP
#define JEFI_JELLY_HPP

#include "jell/buf_handler.hpp"
#include "jell/event_handler.hpp"

#include "jell/jell_address.hpp"
#include "jell/jell_allocator.hpp"
#include "jell/jell_context.hpp"
#include "jell/jell_logger.hpp"

#include "jell/error.hpp"
#include "jell/socket.hpp"
#include "jell/tuple.hpp"

#include "ev/ev++.h"

#include <vector>
#include <queue>

namespace   jell
{

    ///////////////////////////////////////////////////////////////////

    #define PACKET_BOM "jefi"
    #define PACKET_BOM_SIZE  4
    #define PACKET_HEAD_SIZE (PACKET_BOM_SIZE + 4)

    ///////////////////////////////////////////////////////////////////

    class   Jelly
    {
    public  :
        enum AddressType {
            AT_UNKNOWN = 0,
            AT_TCP     = 1,
            AT_UDP     = 2,
            AT_UNIX    = 3,
        };
        typedef enum AddressType    address_type;

        typedef std::string     uri_type;


    public  :
        Jelly();
        ~Jelly();

    public  :
        void    connect(const Address&) throw (JellException);
        void    listen(const Address&) throw (JellException);
        void    shutdown(const Address&) throw (JellException);
        void    shutdown(void) throw (JellException);

        void    run(void) throw (JellException);

    public  :
        void    addBufHandler(BufHandler*) throw (JellException);
        void    addEventHandler(EventHandler*) throw (JellException);

    public  :
        // asynchronous call
        void    send(const Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    recv(Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);

        // synchronous call
        void    sendto(const Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    recvfrom(Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);

    private :
        // asynchronous call
        void    udp_send(const Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    udp_send(const uri_type&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    udp_recv(Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    tcp_send(const uri_type&, Buf&, int timeout_in_seconds = -1) throw (JellException);

        // synchronous call
        void    udp_sendto(const Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    udp_recvfrom(Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    tcp_sendto(const Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);
        void    tcp_recvfrom(Address&, Buf&, int timeout_in_seconds = -1) throw (JellException);

        void    handleIncomingBuf( void ) throw ();

    private :
        address_type    getAddressType(const Address&) const;

    public :
        Logger* jell_logger;
        Logger* logger;

        Allocator*      allocator;

        Context*        context;

        typedef std::vector< BufHandler* >  table_buf_handler_type;
        table_buf_handler_type     m_table_buf_handler;

        typedef std::vector< EventHandler* >    table_event_handler_type;
        table_event_handler_type    m_table_event_handler;

    private :

        // UDP socket handlers
        typedef std::map< uri_type, UDPSocket* > table_udp_listener_type;

        table_udp_listener_type     m_table_udp_listener;
        UDPSocket*  m_udper_p;

        // TCP socket handlers
        typedef std::map< uri_type, TCPServerSocket* >   table_tcp_listener_type;
        typedef std::map< uri_type, TCPSocket* >         table_tcp_transporter_type;

        table_tcp_listener_type     m_table_tcp_listener;
        table_tcp_transporter_type  m_table_tcp_transporter;

        // Buffer
        typedef tuple< Buf*, uri_type >  message_type;
        std::queue< message_type >  m_queue_to_handle;

    ////////////////////////////////////////////////////////////////////
    //  Event Declaration
    //
    private :
        struct   UDPWatcher : public ev::io
        {
            UDPWatcher( UDPSocket* socket_p ) : m_socket_p(socket_p) {}

            UDPSocket*  m_socket_p;

            void    send( message_type& message )
            {
                m_queue_message_out.push( message );
            }

            // buffer for read-in

            // buffer for write-out
            std::queue< message_type >  m_queue_message_out;
        };

        struct   TCPAcceptWatcher : public ev::io
        {
            TCPAcceptWatcher( TCPServerSocket* socket_p, const uri_type& uri )
                : m_socket_p(socket_p), m_uri(uri)
            {}

            TCPServerSocket*  m_socket_p;

            uri_type    m_uri;
        };

        struct   TCPWatcher : public ev::io
        {
            TCPWatcher( TCPSocket* socket_p, const uri_type& uri )
                : m_socket_p(socket_p), m_uri(uri)
            {
                m_head_recv = new unsigned char[PACKET_HEAD_SIZE];
                m_head_send = new unsigned char[PACKET_HEAD_SIZE];

                m_stage_send = 0;
                m_stage_recv = 0;
            }

            ~TCPWatcher()
            {
                if (NULL != m_head_recv) {
                    delete [] m_head_recv;
                }
                if (NULL != m_head_send) {
                    delete [] m_head_send;
                }
            }

            TCPSocket*  m_socket_p;

            void    send( Buf* message )
            {
                m_queue_message_out.push( message );
            }

            // buffer for read-in

            // buffer for write-out
            std::queue< Buf* >  m_queue_message_out;

            uri_type    m_uri;

            // recv related
            int             m_stage_recv;
            size_t          m_size_to_recv;
            void*           m_recv_p;
            unsigned char*  m_head_recv;
            Buf*            m_buf_recv_p;

            // send related
            int             m_stage_send;
            size_t          m_size_to_send;
            void*           m_send_p;
            unsigned char*  m_head_send;
            Buf*            m_buf_send_p;
        };

    private :
        typedef std::map< uri_type, UDPWatcher* >    table_udp_watcher_type;
        typedef table_udp_watcher_type  table_udp_read_watcher_type;
        typedef table_udp_watcher_type  table_udp_write_watcher_type;

        table_udp_read_watcher_type     m_table_udp_read_watcher;
        table_udp_write_watcher_type    m_table_udp_write_watcher;

        typedef std::map< uri_type, TCPAcceptWatcher* > table_tcp_accept_watcher_type;
        table_tcp_accept_watcher_type   m_table_tcp_accept_watcher;

        typedef std::map< uri_type, TCPWatcher* >       table_tcp_watcher_type;
        typedef table_tcp_watcher_type  table_tcp_read_watcher_type;
        typedef table_tcp_watcher_type  table_tcp_write_watcher_type;

        table_tcp_read_watcher_type     m_table_tcp_read_watcher;
        table_tcp_write_watcher_type    m_table_tcp_write_watcher;

    private :
        void    ev_udp_read_cb(ev::io& watcher, int revent) throw();
        void    ev_udp_write_cb(ev::io& watcher, int revent) throw();

        void    ev_tcp_accept_cb(ev::io& watcher, int revent) throw();
        void    ev_tcp_read_cb(ev::io& watcher, int revent) throw();
        void    ev_tcp_write_cb(ev::io& watcher, int revent) throw();

        void    prepareTransportWatcher( UDPSocket*, const std::string& );
        void    prepareListenWatcher( TCPServerSocket*, const std::string& );

        void    registerTransportWatcher( TCPSocket*, const std::string& );
        void    unregisterTransportWatcher( const std::string& );

    private :
        void    ev_signal_stop_cb(ev::sig &watcher, int revents);
        void    ev_idle_cb(ev::idle& watcher, int revents);
        void    ev_prepare_cb(ev::prepare& watcher, int revents);

    private :
        ev::sig     m_ev_signal_stop;
        ev::idle    m_ev_idle;
        ev::prepare m_ev_prepare;

        struct ev_loop*     m_ev_loop;

    };  // class Jelly

}   // namespace jell

#endif  // JEFI_JELLY_HPP
